﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Security;
using System.Web;
using FluentPS.Impersonation;

namespace FluentPS.Services.Impl
{
    public class PsiContextService : IPsiContextService
    {
        private object _lock = new object();
        private readonly Stack<PsiImpersonationContext> _contextStack = new Stack<PsiImpersonationContext>();

        private const string IMPERSONATION_CONTEXT_INFO = "IMPERSONATION_CONTEXT_INFO";

        public PsiContextService()
        {
            InitWebServicesPolicy();
        }

        public static void InitWebServicesPolicy()
        {
            ServicePointManager.ServerCertificateValidationCallback =
                    new RemoteCertificateValidationCallback(
                        delegate
                        {
                            return true;
                        });
        }

        public PsiImpersonationContext GetImpersonationContext()
        {
            lock (_lock)
            {
                Stack<PsiImpersonationContext> currentContextStack;
                if (HttpContext.Current != null)
                    currentContextStack = GetContextStackInHttpContext();
                else
                    currentContextStack = _contextStack;

                return currentContextStack != null && currentContextStack.Count > 0 ? currentContextStack.Peek() : null;
            }
        }

        public PsiImpersonationContext SetImpersonationContext(PsiImpersonationContext impersonationContext)
        {
            lock (_lock)
            {
                if (impersonationContext == null)
                    throw new ArgumentNullException("impersonationContext");

                if (HttpContext.Current != null)
                {
                    Stack<PsiImpersonationContext> httpContextStack = GetContextStackInHttpContext() ??
                                                                          new Stack<PsiImpersonationContext>();
                    httpContextStack.Push(impersonationContext);
                    HttpContext.Current.Items[IMPERSONATION_CONTEXT_INFO] = httpContextStack;
                }
                else
                    _contextStack.Push(impersonationContext);

                return impersonationContext;
            }
        }

        public void RemoveImpersonationContext(PsiImpersonationContext impersonationContext)
        {
            lock (_lock)
            {
                if (HttpContext.Current != null)
                {
                    Stack<PsiImpersonationContext> httpContextStack = GetContextStackInHttpContext();
                    if (httpContextStack != null)
                    {
                        httpContextStack.Pop();
                        HttpContext.Current.Items[IMPERSONATION_CONTEXT_INFO] = httpContextStack;
                    }
                }
                else
                    _contextStack.Pop();
            }
        }

        private Stack<PsiImpersonationContext> GetContextStackInHttpContext()
        {
            return HttpContext.Current != null
                       ? HttpContext.Current.Items[IMPERSONATION_CONTEXT_INFO] != null ?
                            ((Stack<PsiImpersonationContext>)HttpContext.Current.Items[IMPERSONATION_CONTEXT_INFO])
                            :
                            null
                       : null;
        }
    }
}
